#pragma once
#include <iostream>
#include <string>
#include "../comm/util.hpp"
#include "../comm/log.hpp"
// #include "oj_model.hpp"
#include "oj_model2.hpp"
#include "oj_view.hpp"
#include <fstream>
#include<jsoncpp/json/json.h>
#include"../comm/httplib.h"
namespace ns_control
{
    using namespace ns_util;
    using namespace ns_log;
    using namespace ns_model;
    using namespace std;
    using namespace ns_view;
    const string service_machine = "./conf/service_machine.conf";
    class Machine
    {
    public:
        string ip;
        int port;
        uint64_t load;
        mutex *mtx; // mutex禁止拷贝，使用指针

        Machine() : ip(""), port(0), load(0), mtx(nullptr)
        {
        }
        ~Machine()
        {
        }
        void IncLoad()
        {
            if (mtx)
                mtx->lock();
            load++;
            if (mtx)
                mtx->unlock();
        }
        void DecLoad()
        {
            if (mtx)
                mtx->lock();
            load--;
            if (mtx)
                mtx->unlock();
        }
        void ResetLoad()
        {
            if(mtx) mtx->lock();
            load=0;
            if(mtx) mtx->unlock();
        }
        uint64_t Load()
        {
            uint64_t _load = 0;
            if (mtx)
                mtx->lock();
            _load = load;
            if (mtx)
                mtx->unlock();
            return _load;
        }
    };

    class LoadBlance
    {
    private:
        vector<Machine> machines; // 可用服务主机
        vector<int> online;       // 在线主机
        vector<int> offline;      // 离线主机
        mutex mtx;

    public:
        LoadBlance()
        {
            assert(LoadConf(service_machine));
            LOG(INFO) << "加载" << service_machine << "成功" << "\n";
        }
        ~LoadBlance()
        {
        }

        bool LoadConf(const std::string &machine_conf)
        {
            ifstream in(machine_conf);
            if (!in.is_open())
            {
                return false;
            }
            string line;
            while (getline(in, line))
            {
                vector<string> tokens;
                StringUtil::SplitString(line, &tokens, ":");
                if (tokens.size() != 2)
                {
                    LOG(WARNING) << "切分" << line << "失败\n";
                    continue;
                }
                Machine m;
                m.ip = tokens[0];
                m.port = atoi(tokens[1].c_str());
                m.load = 0;
                m.mtx = new mutex();
                online.push_back(machines.size());
                machines.push_back(m);
            }

            in.close();
            return true;
        }
        bool SmartChoice(int *id,Machine **m)
        {
            mtx.lock();
            int online_num=online.size();
            if(online_num==0)
            {
                mtx.unlock();
                LOG(FATAL)<<"所有后端编译主机已经离线，请运维的同事尽快查看"<<"\n";
                return false;
            }

            //遍历找到负载最小的机器
            *id=online[0];
            *m=&machines[online[0]];
            uint64_t min_load=machines[online[0]].Load();
            for(int i=0;i<online_num;i++)
            {
                uint64_t curr_load=machines[online[i]].Load();
                if(min_load>curr_load)
                {
                    min_load=curr_load;
                    *id=online[i];
                    *m=&machines[online[i]];

                }
            }
            mtx.unlock();
            return true;

        }
        void OfflineMachine(int id)
        {
            mtx.lock();
            for(auto iter=online.begin();iter!=online.end();iter++)
            {
                if(*iter==id)
                {
                    machines[id].ResetLoad();
                    online.erase(iter);
                    offline.push_back(id);
                    break;
                }
            }
            mtx.unlock();
        }
        void OnlineMachine()
        {
            mtx.lock();
            online.insert(online.end(),offline.begin(),offline.end());
            offline.erase(offline.begin(),offline.end());
            mtx.unlock();
            LOG(INFO)<<"所有的主机上线了"<<"\n";
        }
        void ShowMachines()
        {
            mtx.lock();
            cout<<"当前在线主机列表:";
            for(auto &id:online)
            {
                cout<<id<<" ";
            }
            cout<<endl;
            cout<<"当前离线主机列表: ";
            for(auto &id:offline)
            {
                cout<<id<<" ";
            }
            cout<<endl;
            mtx.unlock();
        }
    };

    class Control
    {
    private:
        Model model;
        View view;
        LoadBlance loadblance;

    public:
        Control()
        {
        }
        ~Control()
        {
        }
        void RecoveryMachine()
        {
            loadblance.OnlineMachine();
        }
        // 根据题目信息构建网页
        bool AllQuestions(string *html)
        {
            bool ret = true;
            vector<struct Question> all;
            if (model.GetAllQuestions(&all))
            {
                // 获取提取信息成功
                view.AllExpandHtml(all, html);
            }
            else
            {
                *html = "获取题⽬失败, 形成题⽬列表失败";
                ret = false;
            }
            return ret;
        }

        bool Question(const string &number, string *html)
        {
            bool ret = true;
            struct Question q;
            if (model.GetOneQuestion(number, &q))
            {
                view.OneExpandHtml(q, html);
            }
            else
            {
                *html = "指定题目" + number + "不存在";
                ret = false;
            }
            return ret;
        }

        void Judge(const string &number,const string &in_json,string *out_json)
        {
            //拿到题目
            struct Question q;
            model.GetOneQuestion(number,&q);
            //in_json进行反序列化，得到题目id，用户提交的代码，input
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json,in_value);
            string code=in_value["code"].asCString();
            
            //重新拼接用户
            Json::Value compile_value;
            compile_value["input"]=in_value["input"].asCString();
            compile_value["code"]=code+"\n"+q.tail;
            compile_value["cpu_limit"]=q.cpu_limit;
            compile_value["mem_limit"]=q.mem_limit;
            Json::FastWriter writer;
            string compile_string=writer.write(compile_value);

            //选择负载最低的主机
            //一直选择，直到有主机可用，否则就是全部挂掉
            while(true)
            {
                int id=0;
                Machine *m=nullptr;
                if(!loadblance.SmartChoice(&id,&m))
                {
                    break;
                }
                LOG(INFO)<<"选择主机成功，主机id为"<<id<<"详细信息"<<m->ip<<":"<<m->port<<"当前主机的负载"<<m->load<<"\n";

                //发起HTTP请求，得到结果
                httplib::Client cli(m->ip,m->port);
                m->IncLoad();
                if(auto res=cli.Post("/compile_and_run",compile_string,"appilcation/json;charset=utf-8"))
                {
                    if(res->status==200)
                    {
                        *out_json=res->body;
                        m->DecLoad();
                        LOG(INFO)<<"请求编译和运行服务成功...\n";
                        break;
                    }
                    m->DecLoad();
                }
                else
                    {
                        //请求失败
                        LOG(ERROR)<<"当前请求的主机id为:"<<id<<"详细信息"<<m->ip<<":"<<m->port<<"可能已经离线"<<"\n";
                        loadblance.OfflineMachine(id);
                        loadblance.ShowMachines();
                    }
            }
            

        }
    };
}